{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "tags": [
     "notebook-header"
    ]
   },
   "source": [
    "[![GitHub issues by-label](https://img.shields.io/github/issues-raw/pfebrer/sisl/GridPlot?style=for-the-badge)](https://github.com/pfebrer/sisl/labels/GridPlot)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "GridPlot\n",
    "=========\n",
    "\n",
    "`GridPlot` class will help you very easily display any `Grid`. \n",
    "\n",
    "<div class=\"alert alert-info\">\n",
    "    \n",
    "Note\n",
    "    \n",
    "Dedicated software like VESTA might be faster in rendering big 3D grids, but the strength of this plot class lies in its great **flexibility**, **tunability** (change settings as you wish to customize the display from python) **and convenience** (no need to open another software, you can see the grid directly in your notebook). Also, you can **combine your grid plots** however you want **with the other plot classes** in the framework.\n",
    "\n",
    "</div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import sisl\n",
    "import numpy as np\n",
    "\n",
    "# This is just for convenience to retreive files\n",
    "siesta_files = sisl._environ.get_environ_variable(\"SISL_FILES_TESTS\") / \"siesta\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The first thing that we need to do to plot a grid is having a grid, so let's get one!\n",
    "\n",
    "In this case, we are going to use an electronic density grid from a `.RHO` file in SIESTA. There are multiple ways of getting the plot:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "rho_file = siesta_files / \"SrTiO3\" / \"unpolarized\" / \"SrTiO3.RHO\"\n",
    "\n",
    "# From a sisl grid, you can do grid.plot()\n",
    "grid = sisl.get_sile(rho_file).read_grid()\n",
    "plot = grid.plot()\n",
    "\n",
    "# All siles that implement read_grid can also be directly plotted\n",
    "plot = sisl.get_sile(rho_file).plot.grid()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Anyway, you will end up having the grid under `plot.grid`.\n",
    "\n",
    "Let's see what we've got:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "plot"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Well, this doesn't look much like a grid, does it? By default, `GridPlot` only shows the third axis of the grid, while reducing along the others. This is because 3D representations can be quite heavy depending on the computer you are in, and it's better to be safe :)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Plotting in 3D, 2D and 1D\n",
    "\n",
    "Much like [GeometryPlot](GeometryPlot.ipynb), you can select the axes of the grid that you want to display. In this case, the other axes will be reduced.\n",
    "\n",
    "For example, if we want to see the xy plane of the electronic density, we can do: "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "plot.update_inputs(axes=\"xy\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "3d representations of a grid will display isosurfaces:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "plot.update_inputs(axes=\"xyz\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Specifying the axes\n",
    "----\n",
    "You may want to see your grid in different ways. The most common one is to display the cartesian coordinates. You indicate that you want cartesian coordinates by passing `{\"x\", \"y\", \"z\"}`. You can pass them as a list or as a multicharacter string:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "plot.update_inputs(axes=\"xy\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The order of the axes is relevant, although in this case we will not see much difference:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "plot.update_inputs(axes=\"yx\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Sometimes, to inspect the grid you may want to see the values in fractional coordinates. In that case, you should pass `{\"a\", \"b\", \"c\", \"0\", \"1\", \"2\", 0, 1, 2}`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "plot.update_inputs(axes=\"ab\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "To summarize the different possibilities:\n",
    "\n",
    "- `{\"x\", \"y\", \"z\"}`: The **cartesian coordinates** are displayed.\n",
    "- `{\"a\", \"b\", \"c\"}`: The **fractional coordinates** are displayed. Same for {0,1,2}.\n",
    "\n",
    "<div class=\"alert alert-warning\">\n",
    "    \n",
    "Some non-obvious behavior\n",
    "    \n",
    "**You can not mix cartesian axes with lattice vectors**. Also, for now, the **3D representation only displays cartesian coordinates**.\n",
    "\n",
    "</div>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Dimensionality reducing method\n",
    "\n",
    "As we mentioned, the dimensions that are not displayed in the plot are reduced. The setting that controls how this process is done is `reduce_method`, which can be either `\"average\"` or `\"sum\"`."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can test the different reducing methods in a 1D representation to see the effects:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "plot.update_inputs(axes=\"z\", reduce_method=\"average\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "plot.update_inputs(reduce_method=\"sum\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You can see that the values obtained are very different. Your analysis **might be dependent on the method used to reduce the dimensionality**, be careful!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "plot = plot.update_inputs(axes=\"xyz\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Isosurfaces and contours\n",
    "\n",
    "There's one parameter that controls both the display of isosurfaces (in 3d) and contours (in 2d): `isos`.\n",
    "\n",
    "`isos` is a list of dicts where each dict asks for an isovalue. The possible keys are:\n",
    "\n",
    "- `val`: Value where to draw the isosurface.\n",
    "- `frac`: Same as value, but indicates the fraction between the minimum and maximum value, useful if you don't know the range of the values.\n",
    "- `color`, `opacity`: control the aesthetics of the isosurface.\n",
    "- `name`: Name of the isosurface, e.g. to display on the plot legend."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If no `isos` is provided, 3d representations plot the 0.3 and 0.7 (`frac`) isosurfaces. This is what you can see in the 3d plot that we displayed above.\n",
    "\n",
    "Let's play a bit with `isos`. The first thing we will do is to change the opacity of the outer isosurface, since there's no way to see the inner one right now (although you can toggle it by clicking at the legend, courtesy of plotly :))."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "plot.update_inputs(isos=[{\"frac\": 0.3, \"opacity\": 0.4}, {\"frac\": 0.7}])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now we can see all the very interesting features of the inner isosurface! :)\n",
    "\n",
    "Let's now see how contours look in 2d:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "plot.update_inputs(axes=\"xy\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Not bad."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Volumetric display\n",
    "----\n",
    "\n",
    "Using isosurfaces in the 3D representation, one can achieve a sense of volumetric data. We can do so by asking for **multiple isosurfaces an setting the opacity** of each one properly.\n",
    "\n",
    "You can play with it and do the exact thing that you wish. For example, we can represent isosurfaces at increasing values, with the higher values being more opaque:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "plot.update_inputs(\n",
    "    axes=\"xyz\",\n",
    "    isos=[\n",
    "        {\"frac\": frac, \"opacity\": frac / 2, \"color\": \"green\"}\n",
    "        for frac in np.linspace(0.1, 0.8, 20)\n",
    "    ],\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The more surfaces you add, the more sense of depth you'll acheive. But of course, it will be more expensive to render.\n",
    "\n",
    "<div class=\"alert alert-info\">\n",
    "    \n",
    "Note\n",
    "    \n",
    "Playing with colors (e.g. setting a colorscale) and not just opacities might give you even a better sense of depth. A way of automatically handling this for you might be introduced in the future.\n",
    "\n",
    "</div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "plot = plot.update_inputs(axes=\"xy\", isos=[])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Colorscales\n",
    "\n",
    "You might have already seen that 2d representations use a colorscale. You can change it with the `colorscale` setting."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "plot.update_inputs(colorscale=\"temps\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "And you can control its range using `crange` (min and max bounds of the colorscale) and `cmid` (middle value of the colorscale, will take preference over `crange`). In this way, you are able **saturate the display as you wish**. \n",
    "\n",
    "For example, in this case, if we bring the lower bound up enough, we will be able to hide the Sr atoms that are in the corners. But be careful not to make it so high that you hide the oxygens as well!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "plot.update_inputs(crange=[50, 200])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Using only part of the grid\n",
    "\n",
    "As we can see in the 3d representation of the electronic density, there are two atoms contributing to the electronic density in the center, that's why we get such a big difference in the 2d heatmap.\n",
    "\n",
    "We can use the `x_range`, `ỳ_range` and `z_range` settings to take into account only a certain part of the grid. for example, in this case we want only the grid where `z` is in the `[1,3]` interval so that we remove the influence of the oxygen atom that is on top."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "plot.update_inputs(z_range=[1, 3])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now we are seeing the real difference between the Ti atom (in the center) and the O atoms (at the edges)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Applying transformations\n",
    "\n",
    "One can apply as many transformations as it wishes to the grid by using the `transforms` setting. \n",
    "\n",
    "It should be a list where each item can be a string (the name of the function, e.g. `\"numpy.sin\"`) or a function. Each transform is passed to `Grid.apply`, see the method for more info."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "plot.update_inputs(transforms=[abs, \"numpy.sin\"], crange=None)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Notice that the order of the transformations matter:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "plot.update_inputs(transforms=[\"sin\", abs], crange=None)\n",
    "# If a string is provided with no module, it will be interpreted as a numpy function\n",
    "# Therefore \"sin\" == \"numpy.sin\" and abs != \"abs\" == \"numpy.abs\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Visualizing supercells\n",
    "\n",
    "Visualizing grid supercells is as easy as using the `nsc` setting. If we want to repeat our grid visualization along the second axis 3 times, we just do:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": [
     "nbsphinx-thumbnail"
    ]
   },
   "outputs": [],
   "source": [
    "plot.update_inputs(axes=\"yx\", nsc=[1, 3, 1], z_range=[1.7, 1.9]).show(\"png\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "tags": [
     "notebook-end"
    ]
   },
   "source": [
    "We hope you enjoyed what you learned!"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
